﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Net;
using System.Threading;
using System.Net.Sockets;               //SOCKET命名空间
using System.Collections;               //ARRLIST命名空间

namespace Server
{
    public partial class Form1 : Form
    {
        private Socket clientSocket;                        //用于接受来自客户端的连接请求
        private Thread clientService;                       //定义一个线程，用于对应一个客户的请求 

        private const int Port = 3505;                      //监听端口


        public static ArrayList HandleArray = new ArrayList();                    //声明记录上线Winsock句柄的数组
        public Dictionary<string,Socket> sDty = new Dictionary<string,Socket>();

        private delegate void Add_Invoke(ListViewItem lst);             //列表项增加委托函数声明
        private delegate void Del_Invoke(string handle);      //列表项修改委托函数声明

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            lstViewServer.View = View.Details;
            int lngWidth = lstViewServer.Width;
            lstViewServer.Columns.Add("序号", lngWidth * 1 / 15, HorizontalAlignment.Center);
            lstViewServer.Columns.Add("计算机名", lngWidth * 4 / 15, HorizontalAlignment.Center);
            lstViewServer.Columns.Add("IP", lngWidth * 4 / 15, HorizontalAlignment.Center);
            lstViewServer.Columns.Add("句柄", lngWidth * 2 / 15, HorizontalAlignment.Center);
            lstViewServer.Columns.Add("端口号", lngWidth * 2 / 15, HorizontalAlignment.Center);
            lstViewServer.Columns.Add("上线时间", lngWidth * 3 / 15, HorizontalAlignment.Center);
            lstViewServer.FullRowSelect = true;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Thread ThrWinsockListen = new Thread(new ThreadStart(WinsockListen));        //创建Winsock监听线程
            ThrWinsockListen.IsBackground = true;
            ThrWinsockListen.Start();

            button1.Text = "正在监听";
            button1.Enabled = false;
        }


        public void WinsockListen()
        {
            IPHostEntry ipHN = new IPHostEntry();
            ipHN = Dns.GetHostByName(Dns.GetHostName());
            string ServerIP = ipHN.AddressList[0].ToString();           //得到本机IP

            IPAddress ip = IPAddress.Parse(ServerIP);
            IPEndPoint iep = new IPEndPoint(ip, Port);
            Socket stServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);      
            byte[] byteMessage = new byte[1024];
            stServer.Bind(iep);             //绑定端口
            stServer.Listen(0);

            while (true)
            {
                try
                {
                    clientSocket = stServer.Accept();                                       //侦听到一个新传入的连接

                    clientService = new Thread(new ThreadStart(ServiceClient));             //启动一个线程用来服务些SOCKET对象(客户端)
                    clientService.IsBackground = true;                  //线程声明为后台线程
                    clientService.Start();
                }
                catch
                {
                    continue;
                }
            }
        }



        private void ServiceClient()
        {
            bool keepalive = true;                          //各个客户端Socket线程存活的标识   
            string socketFlag = "";                         //各个客户端Socket标识
            EndPoint endPoint = null;

            Socket childClient = clientSocket;              //将新传入的客户对象赋给新的SOCKET
            while (keepalive)
            {
                try
                {
                    Byte[] byBuff = new Byte[8192];                                     //声明缓冲区大小
                    childClient.Receive(byBuff, byBuff.Length, 0);
                    string strBuff = Encoding.UTF8.GetString(byBuff).TrimEnd('\0');  //接收到客户端的数据

                    //if (strBuff == "")                                      //如果对方(客户端)为VB下的WINSOCK控件,退出时不能及时响应,故只有在此做判断
                    //{                                                       //如果为对方为C#下,则不需此段判断语句
                    //    childClient.Disconnect(false);
                    //    keepalive = false;
                    //    Socket_Connection(1, endPoint, socketFlag);
                    //}

                    socketFlag = childClient.Handle.ToString();         //得到此SOCKET的句柄,用做SOCKET标识
                    endPoint = childClient.RemoteEndPoint;              //客户端IP+端口

                    if (strBuff.Length >= 3)
                    {
                        string sFg = strBuff.Substring(0, 3);

                        if (sFg == "LOD")       //客户端上线:客户端上线时要发送LOD标识
                        {
                            HandleArray.Add(socketFlag);                    //加入SOCKET句柄到数组列表中
                            sDty.Add(socketFlag,childClient);
                            Socket_Connection(0, endPoint, socketFlag);                    //调用上线处理函数
                        }
                        else if (sFg == "OFF")  //客户端下线:客户端下线时发送OFF标识;此处为正常退出;如果非正常退出,则在出错里进行处理,见下面的出错处理程序
                        {
                            HandleArray.Remove(socketFlag);                 //将此SOCKET移出数组列表
                            sDty.Remove(socketFlag);
                            childClient.Disconnect(false);
                            keepalive = false;

                            Socket_Connection(1, endPoint, socketFlag);
                        }
                        else       //我定义的客户端数据发送格式为#数据#数据#数据:数据的内容为我们想要得到的内容,#为包完整标识符,如果不需要可以不用这么麻烦
                        {           //我使用的数据格式为Ref:1;2;3;4;5
                            int index = HandleArray.IndexOf(socketFlag);            //从句柄列表中搜索此SOCKET实例句柄,如果存在,说明此连接为信任程序

                            if (index >= 0)        //如果存在于主机列表中
                            {
                                //此处加入你存放数据的语句,我是把他放入了一个队列中,用做缓冲,因为我在一秒钟可能会接收到上千条数据,如果没有这么快可以不用缓冲
                                //m_Queue.Push(strBuff);                    //将数据压入队列
                                foreach(KeyValuePair<string,Socket> sp in sDty)
                                {
                                    if (sp.Key != socketFlag)
                                    {
                                        byte[] message = Encoding.UTF8.GetBytes(Dns.GetHostEntry(((IPEndPoint)endPoint).Address).HostName + ":" + strBuff);
                                        sp.Value.Send(message);
                                    }
                                }
                            }
                        }
                    }
                }
                catch (SocketException se)
                {
                    if (se.ErrorCode == 10054)              //SOCKET客户端非正常退出
                    {
                        HandleArray.Remove(socketFlag);                 //将此SOCKET移出数组列表
                        childClient.Disconnect(false);
                        keepalive = false;

                        Socket_Connection(1, endPoint, socketFlag);
                    }
                    else
                    {
                        continue;
                    }
                }

            }

        }


        //函数说明:当有主机连接或关闭时,更新界面信息提示栏
        //输入参数:flag-0:上线;1-下线;ep-IP结构;hd-当前连接句柄
        private void Socket_Connection(int flag, EndPoint ep, string hd)
        {
            IPEndPoint IPpoint = ((IPEndPoint)ep);

            string hostIP = IPpoint.Address.ToString();                        //IP地址
            string hostPort = IPpoint.Port.ToString();                         //端口号

            IPHostEntry myHost = Dns.GetHostByAddress(hostIP);
            string hostName = myHost.HostName;                                  //主机名

            int index = lstViewServer.Items.Count + 1;
            ListViewItem lstItem = new ListViewItem(index.ToString());

            if (flag == 0)          //上线
            {
                lstItem.SubItems.Add(hostName);
                lstItem.SubItems.Add(hostIP);
                lstItem.SubItems.Add(hd);
                lstItem.SubItems.Add(hostPort);
                lstItem.SubItems.Add(DateTime.Now.ToString());
                lstAdd(lstItem);
            }
            else
            {
                lstDel(hd);
            }
        }



        //**********************添加列表项**************************************
        //函数说明:增加委托函数入口
        //传入参数:lstItem-列表各项
        public void lstAdd(ListViewItem lstItem)
        {
            Add_Invoke meInvoke = new Add_Invoke(AddListItem);
            this.Invoke(meInvoke, new object[] { lstItem });
        }

        //函数说明:委托实现函数
        //传入参数:lstItem-列表各项
        private void AddListItem(ListViewItem lst)
        {
            lstViewServer.Items.Add(lst);
        }


        //*************************删除列表项**********************************

        //函数说明:删除委托函数入口
        //传入参数:lstItem-列表各项
        public void lstDel(string handle)
        {
            Del_Invoke meInvoke = new Del_Invoke(DelListItem);
            this.Invoke(meInvoke, new object[] { handle });
        }

        //函数说明:委托实现函数
        //传入参数:lstItem-列表各项
        private void DelListItem(string handle)
        {
            ListViewItem lstItem = lstViewServer.FindItemWithText(handle);
            lstViewServer.Items.Remove(lstItem);

        }


    }
}